#pragma  once

#include "CImg.h"
#include "ImageEx.h"
#include "Timer.h"
#include "sfu_canny_edge.h"

#include "..\\Loirey\\loirey_GLOBAL.h"
#include "..\\Loirey\\loirey_MyMath.h"

using namespace cimg_library;
using namespace loirey;

//class CRichImage
//{
//public:
//	int W,H,WH;
//	CImg<double> imgRGB;
//	CImg<double> imgLab;
//
//public:
//	void LoadImageByFileName(const string& strFN_SourceImage);
//	void LoadImageByFileName(const char* strFN_SourceImage);
//
//	template<typename T>
//	void LoadImage(const CImg<T>& SourceImage, int z = 0)
//	{
//		int ix,iy,iv,tZ,tV,jv;
//		T* qd;
//		double* pd;
//
//		W = SourceImage.dimx();
//		H = SourceImage.dimy();
//		tZ = SourceImage.dimz();
//		tV = SourceImage.dimv();
//		WH = W * H;
//		imgRGB.assign(W, H, 1, 3);
//
//		pd = imgRGB.data;
//		for ( jv = 0, iv = 0; iv < 3; ++iv )
//		{
//			qd = SourceImage.data + z * WH + jv * WH * tZ;
//			for (iy = 0; iy < H; ++iy) for (ix = 0; ix < W; ++ix)
//				*(pd++) = (double)(*(qd++));
//			if (jv+1<tV) ++jv;
//		}
//
//		imgLab.assign(imgRGB, false);
//		imgLab.RGBtoLab();
//	}
//};

class CExternalTools
{
public:
	static CTimer le_myTimer_A;
	static CTimer le_myTimer_B;
	static CTimer le_myTimer_C;
	static CTimer le_myTimer_D;

	static void ResetAllTimers()
	{
		le_myTimer_A.Reset();
		le_myTimer_B.Reset();
		le_myTimer_C.Reset();
		le_myTimer_D.Reset();
	}
protected:
	static int _public_color_for_cimg[3];
public:
	class ConsoleTools
	{
	public:
		static void MakeDirectory(
			string strPN_TargetDirectory, 
			bool fClean = false, string strPN_RunningBase = "");
		static int RunDosCommand(
			string strPN_WorkPath,
			string strCommandLine,
			string strPN_ExeFile = ""
			);
		static int RunDosCommand(
			string strCommandLine);
		static void ClearCurrentLine();
		static void MakeCurrentLine(string strContext, bool fPrintEnter);
		static void OutputTextToStream(ostream& outStream, string strContext, bool fPrintEnter, bool fFlush = true);
		template <typename T> static string FormatReal(T Num, const char* Format)
		{
			static char fm[10000];
			static char sn[10000];
			double n;

			if (Num > +EPS)
				n = (double)((T)(Num + EPS));
			else if (Num < -EPS)
				n = (double)((T)(Num - EPS));
			else
				n = (double)((T)EPS);
			sprintf(fm,"%%%slf",Format);
			sprintf(sn,fm,n);
			return sn;
		}
	};
public:
	static const char* GetTimeText(double time_secs)
	{
		static char strTimeText[10001];

		if (time_secs < 60.0 * 0.9)
			sprintf_s(strTimeText, "%5.2lf secs", time_secs);
		else if (time_secs < 3600.0 * 0.9)
			sprintf_s(strTimeText, "%5.2lf mins", time_secs / 60.0);
		else if (time_secs < 86400.0 * 0.9)
			sprintf_s(strTimeText, "%4.1lf hours", time_secs / 3600.0);
		else
			sprintf_s(strTimeText, "%5.1lf days", time_secs / 86400.0);

		return strTimeText;
	}

	template <typename T>
	static size_t GetMemoryUsage(const CImg<T>& instance_of_CImg)
	{
		size_t ret = sizeof(unsigned int) * 4 + sizeof(bool) + sizeof(T*);
		if (!instance_of_CImg.is_shared)
			ret += sizeof(T) * instance_of_CImg.width * instance_of_CImg.height * instance_of_CImg.depth * instance_of_CImg.dim;
		return ret;
	}
	static int GetIntRGB(int R, int G, int B)
	{
		return ((R<<16) | (G<<8) | B);
	}
	static const int* GetIntArrayRGB(int ColorRGB)
	{
		_public_color_for_cimg[0] = ((ColorRGB>>16)&255);
		_public_color_for_cimg[1] = ((ColorRGB>>8)&255);
		_public_color_for_cimg[2] = (ColorRGB&255);
		return _public_color_for_cimg;
	}
	static const int* GetIntArrayRGB(int Red, int Green, int Blue)
	{
		_public_color_for_cimg[0] = Red;
		_public_color_for_cimg[1] = Green;
		_public_color_for_cimg[2] = Blue;
		return _public_color_for_cimg;
	}
	static void DrawRectBox(CImg<int>& imgDst, const CRectBox<int> Box, int ColorRGB)
	{
		DrawRectBox(imgDst, Box, ((ColorRGB>>16)&255), ((ColorRGB>>8)&255), (ColorRGB&255));
	}
	static void DrawRectBox(CImg<int>& imgDst, const CRectBox<int> Box, int R, int G, int B)
	{
		int tr = _public_color_for_cimg[0];
		int tg = _public_color_for_cimg[1];
		int tb = _public_color_for_cimg[2];
		_public_color_for_cimg[0] = R;
		_public_color_for_cimg[1] = G;
		_public_color_for_cimg[2] = B;

		imgDst.draw_line(Box.x, Box.y, Box.x+Box.w-1, Box.y, _public_color_for_cimg);
		imgDst.draw_line(Box.x, Box.y, Box.x, Box.y+Box.h-1, _public_color_for_cimg);
		imgDst.draw_line(Box.x+Box.w-1, Box.y, Box.x+Box.w-1, Box.y+Box.h-1, _public_color_for_cimg);
		imgDst.draw_line(Box.x, Box.y+Box.h-1, Box.x+Box.w-1, Box.y+Box.h-1, _public_color_for_cimg);

		_public_color_for_cimg[0] = tr;
		_public_color_for_cimg[1] = tg;
		_public_color_for_cimg[2] = tb;
	}
	template <typename T>
	static void EnlargeImage(CImg<T>& Dst, const CImg<T>& Src, int xMargin, int yMargin, int z = 0)
	{
		int aw, ah, av, awh;
		int bw, bh, bwh;
		int cx, cy;
		int ix,iy,iv;
		T* pd;
		const T* qd;

		//cout<<" "<<Src.dimx()<<" "<<Src.dimy()<<" "<<xMargin<<" "<<yMargin<<endl;

		aw = Src.dimx(); bw = aw + xMargin * 2; cx = aw + xMargin - 1;
		ah = Src.dimy(); bh = ah + yMargin * 2; cy = ah + yMargin - 1;
		av = Src.dimv(); awh = aw * ah; bwh = bw * bh;

		if (awh*av<1 || z<0 || z>=Src.dimz()) return;
		if (xMargin<0) xMargin = 0;
		if (yMargin<0) yMargin = 0;

		T* bkp = new T[awh * av];
		pd = bkp;
		for ( iv = 0; iv < av; ++iv )
		{
			qd = Src.data + awh * Src.dimz() * iv;
			for ( ix = 0; ix < awh; ++ix )
				*(pd++) = *(qd++);
		}

		Dst.assign(bw, bh, 1, av);
		for ( iv = 0; iv < av; ++iv )
		{
			iv = iv;
			for ( iy = cy; iy >= yMargin; --iy )
			{
				qd = bkp + awh * iv + aw * (iy + 1 - yMargin) - 1;
				pd = Dst.data + bwh * iv + bw * (iy + 1) - 1;
				ix = bw - 1;
				for ( ; ix > cx; --ix )
					*(pd--) = *qd;
				for ( ; ix > xMargin; --ix )
					*(pd--) = *(qd--);
				for ( ; ix >= 0; --ix )
					*(pd--) = *qd;
			}
			for ( iy = 0; iy < yMargin; ++iy )
			{
				pd = Dst.data + bwh * iv + bw * iy;
				qd = Dst.data + bwh * iv + bw * yMargin;
				for ( ix = 0; ix < bw; ++ix )
					*(pd++) = *(qd++);
			}
			for ( iy = cy + 1; iy < bh; ++iy )
			{
				pd = Dst.data + bwh * iv + bw * iy;
				qd = Dst.data + bwh * iv + bw * cy;
				for ( ix = 0; ix < bw; ++ix )
					*(pd++) = *(qd++);
			}
		}

		delete[] bkp;
	}
	
	static void OutputTextToImage(
		CImg<int>& dstImgRGB,
		string strText, int x, int y,
		int BgColorR, int BgColorG, int BgColorB,
		int FgColorR, int FgColorG, int FgColorB,
		int FontSize = 10
		);
	static void OutputRectBoxToImage(
		CImg<int>& dstImgRGB,
		const CRectBox<int>& Box,
		int ColorR, int ColorG, int ColorB, int BorderWidth,
		int FontSize = 10, string Title = ""
		);

	static void MakeCImageExInstance(CImageEx& DstImageEx, const CImg<int>& imgSrc)
	{
		if (DstImageEx.IsValid())
			DstImageEx.Unload();
		DstImageEx.m_pBitmap	= new Bitmap(imgSrc.width, imgSrc.height, PixelFormat32bppPARGB);

		RGBQUAD		*pBits	= (RGBQUAD*) DstImageEx.GetBits();

		const int* qd_R = imgSrc.data + 0 * imgSrc.width * imgSrc.height;
		const int* qd_G = imgSrc.data + 1 * imgSrc.width * imgSrc.height;
		const int* qd_B = imgSrc.data + 2 * imgSrc.width * imgSrc.height;
		for ( int i = 0; i < (int)imgSrc.height; i++ )
		{
			for ( int j = 0; j < (int)imgSrc.width; j++, pBits++ )
			{
				pBits->rgbRed		= *(qd_R++);
				pBits->rgbGreen		= *(qd_G++);
				pBits->rgbBlue		= *(qd_B++);
				pBits->rgbReserved	= 255;
			}
		}

		DstImageEx.FreeBits();
	}

	template <typename T>
	static void myLoadImage(string strImageFileName, CImg<T>& dstImage)
	{
		CImageEx *ptrTmpImg = new CImageEx();
		ptrTmpImg->Load(strImageFileName.c_str());
		ptrTmpImg->ConvertToARGB32Bits();
		RGBQUAD *pBits = (RGBQUAD*)(ptrTmpImg->GetBits());
		int w = ptrTmpImg->GetWidth();
		int h = ptrTmpImg->GetHeight();
		dstImage.assign(w,h,1,3);
		int x,y;
		for ( y = 0; y < h; ++y )
		{
			for ( x = 0; x < w; ++x )
			{
				dstImage.at(x,y,0,0) = (T)pBits->rgbRed;
				dstImage.at(x,y,0,1) = (T)pBits->rgbGreen;
				dstImage.at(x,y,0,2) = (T)pBits->rgbBlue;
				++pBits;
			}
		}
		ptrTmpImg->FreeBits();
		delete ptrTmpImg;
	}

	template <typename T>
	static void mySaveImage(string strImageFileName, CImg<T>& srcImage)
	{
		CImageEx *ptrTmpImg = new CImageEx();
		ptrTmpImg->m_pBitmap = new Bitmap(srcImage.dimx(), srcImage.dimy(), PixelFormat32bppPARGB);
		RGBQUAD *pBits = (RGBQUAD*)(ptrTmpImg->GetBits());
		int w = srcImage.dimx();
		int h = srcImage.dimy();
		int x,y,v0,v1,v2;
		
		if (srcImage.dimv() > 0) v0 = 0;
		if (srcImage.dimv() > 1) v1 = 1; else v1 = v0;
		if (srcImage.dimv() > 2) v2 = 2; else v2 = v1;

		for ( y = 0; y < h; ++y )
		{
			for ( x = 0; x < w; ++x )
			{
				pBits->rgbRed = int(srcImage.at(x,y,0,v0) + 0.5);
				pBits->rgbGreen = int(srcImage.at(x,y,0,v1) + 0.5);
				pBits->rgbBlue = int(srcImage.at(x,y,0,v2) + 0.5);
				pBits->rgbReserved = 255;
				++pBits;
			}
		}
		ptrTmpImg->FreeBits();
		ptrTmpImg->Save(strImageFileName.c_str());
		delete ptrTmpImg;
	}

	template <typename T>
	static void GetSingleDimValue(CImg<T> &imgDst, const CImg<T> &imgSrc, int Dim)
	{
		int	DimX	= imgSrc.dimx();
		int	DimY	= imgSrc.dimy();
		int	DimZ	= imgSrc.dimz();

		imgDst.assign(DimX, DimY, DimZ);
		for ( int x = 0; x < DimX; x++ )
			for ( int y= 0; y < DimY; y++ )
				for ( int z = 0; z < DimZ; z++ )
					imgDst.at(x,y,z)	= imgSrc(x,y,z,Dim);
	}
	template <typename T>
	static void WriteGrayscaleImageToBinaryFile(const CImg<T>& GrayscaleImage, string strFileName)
	{
		int _dimx = GrayscaleImage.dimx();
		int _dimy = GrayscaleImage.dimy();
		int _size = _dimx * _dimy;
		FILE* FOUT = fopen(strFileName.c_str(),"wb");
		fwrite(&_dimx,sizeof(_dimx),1,FOUT);
		fwrite(&_dimy,sizeof(_dimy),1,FOUT);
		fwrite(GrayscaleImage.data,sizeof(GrayscaleImage.data[0]),_size,FOUT);
		fclose(FOUT);
	}
	template <typename T>
	static void WriteGrayscaleImageToTextFile(const CImg<T>& GrayscaleImage, string strFileName)
	{
		int _dimx = GrayscaleImage.dimx();
		int _dimy = GrayscaleImage.dimy();
		int x,y;
		T* ptr;
		ofstream outStream(strFileName.c_str());
		outStream<<_dimx<<endl;
		outStream<<_dimy<<endl;
		ptr = GrayscaleImage.data;
		outStream.precision(6);
		for ( y = 0; y < _dimy; ++y )
		{
			for ( x = 0; x < _dimx; ++x )
				outStream<<' '<<setw(10)<<*(ptr++);
			outStream<<endl;
		}
		outStream.clear();
		outStream.close();
	}
	template <typename T>
	static void OutputByFileExt(const CImg<T>& GrayscaleImage, string strFileName)
	{
		string strExt = strFileName.substr(strFileName.find_last_of("\\/.:")+1);
		int i = (int) strExt.length();
		for (--i;i>=0;--i)
			if ((strExt[i]>='A') && (strExt[i]<='Z'))
				strExt[i] += 'a'-'A';

		if (strExt == "txt")
		{
			WriteGrayscaleImageToTextFile(GrayscaleImage,strFileName);
		}
		else
		{
			WriteGrayscaleImageToBinaryFile(GrayscaleImage,strFileName);
		}
	}
	template <typename T>
	static void NormalizeToGeneralIntGrayscaleImage(const CImg<T>& SrcGrayscaleImage, CImg<int>& imgDst, double MinValue, double MaxValue, bool fAssignNew = true)
	{
		int _dimx = SrcGrayscaleImage.dimx();
		int _dimy = SrcGrayscaleImage.dimy();
		double tt;
		int x, y, t;

		if (fAssignNew) imgDst.assign(_dimx,_dimy);
		MaxValue = (MaxValue - MinValue) / 255.0f;
		for ( y = 0; y < _dimy; ++y )
			for ( x = 0; x < _dimx; ++x )
			{
				tt = SrcGrayscaleImage.at(x, y);
				tt = (tt - MinValue) / MaxValue;
				t = CMyMath::TranslateNumber_Round(tt, (int)0);
				imgDst.at(x, y) = (t<0 ? 0 : (t>255 ? 255 : t));
			}
	}
	template <typename T>
	static void NormalizeToGeneralIntGrayscaleImage(const CImg<T>& SrcGrayscaleImage, CImg<int>& imgDst, bool fAssignNew = true)
	{
		int _dimx = SrcGrayscaleImage.dimx();
		int _dimy = SrcGrayscaleImage.dimy();
		if (fAssignNew) imgDst.assign(_dimx,_dimy);
		T vmin, vmax = SrcGrayscaleImage.maxmin(vmin);
		NormalizeToGeneralIntGrayscaleImage(SrcGrayscaleImage, imgDst, vmin, vmax, fAssignNew);
	}

	private:
	class C_idPair
	{
	public:
		int pos;
		double score;

	public:
		bool operator< (const C_idPair& AnotherInstance) const
		{
			return score < AnotherInstance.score;
		}
	};

	public:
	template <typename T>
	static void DoHistogramEqualization(CImg<int>& DstImgInt, const CImg<T>& SrcImgGray)
	{
		CSimpleTypeArray<C_idPair> _idPair_List;
		int W = SrcImgGray.width;
		int H = SrcImgGray.height;
		int WH = W * H;
		_idPair_List.Resize(WH); _idPair_List.Clear();
		int i;
		C_idPair t_idPair;
		for ( i = 0; i < WH; ++i )
		{
			t_idPair.pos = i;
			t_idPair.score = (double)SrcImgGray.data[i];
			_idPair_List.PushBack(t_idPair);
		}
		DstImgInt.assign(W, H, 1, 1);
		sort(_idPair_List.Begin(), _idPair_List.End());
		for ( i = 0; i < WH; ++i )
		{
			t_idPair = _idPair_List.pElement[i];
			DstImgInt.data[t_idPair.pos] = i*256/(int)WH;
		}
	}

	template <typename T>
	static void NormalizeToGeneralIntGrayscaleImage(const CImg<T>& SrcGrayscaleImage, CImg<unsigned char>& imgDst)
	{
		int _dimx = SrcGrayscaleImage.dimx();
		int _dimy = SrcGrayscaleImage.dimy();
		int x,y;

		imgDst.assign(_dimx,_dimy);
		T vmin, vmax = SrcGrayscaleImage.maxmin(vmin);
		vmax -= vmin;
		for ( y = 0; y < _dimy; ++y )
			for ( x = 0; x < _dimx; ++x )
				imgDst.at(x,y) = (unsigned char)(255.0f*(SrcGrayscaleImage.at(x,y)-vmin)/vmax+.5);
	}

	template <typename TData>
	static void Expand_2D_SampledData(CImg<TData>& imgDstData, CImg<int>& imgVisit)
	{
		CSimpleTypeArray<int> Queue;
		int qh, qt;
		int W = imgDstData.width;
		int H = imgDstData.height;
		int WH = W * H;
		int DD = imgDstData.depth * imgDstData.dim;
		int i, ix, iy;
		int t, tx, ty;
		int j, si, o;

		Queue.Clear(); qt = 0;
		for ( i = 0; i < WH; ++i )
		{
			if (imgVisit.data[i] < 0) continue;
			imgVisit.data[i] = i;
			Queue.PushBack(i); ++qt;
		}

		for ( qh = 0; qh < qt; ++qh )
		{
			i = Queue.pElement[qh];
			ix = i % W;
			iy = i / W;
			si = imgVisit.data[i];
			for ( j = 0; j < 4; ++j )
			{
				tx = ix + (j==0 ? -1 : (j==1 ? +1 : 0));
				ty = iy + (j==2 ? -1 : (j==3 ? +1 : 0));
				t = tx + ty * W;
				if (tx<0 || tx>=W || ty<0 || ty>=H || imgVisit.data[t]>=0) continue;
				
				for ( o = 0; o < DD; ++o )
					imgDstData.data[t + o * WH] = imgDstData.data[si + o * WH];
				imgVisit.data[t] = si;
				Queue.PushBack(t); ++qt;
			}
		}
	}
};

class CSimpleMixedGaussianDistribution
{
public:
	int W, H;
	CImg<double> imgPDF;

public:
	void myInit(int W, int H);
	void Clear();
public:
	void AddNewGaussian(double weight, double ux, double uy, double vx, double vy);
};

